
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "miniutil/miniconv.h"
#include "miniutil/strutil.h"
#include "miniutil/diru.h"

#ifndef WIN32

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#else

#include "winsock2.h"
#include "shlobj.h" 
#include <io.h>
#include <Fcntl.h>

#endif

#include <stdio.h>

using namespace std;
using namespace miniutil;

#ifdef WIN32
const char * diru::SEP = "\\";
#else
const char * diru::SEP = "/";
#endif


diru::diru(const string &dirname) : dirName(dirname), dirHandle(NULL)
{
}

diru::diru(const char *dirname) : dirName(dirname), dirHandle(NULL)
{
}

diru::~diru()
{
	_close();
}

void diru::_close()
{
		if ( dirHandle != NULL )
		{
#ifdef WIN32
			FindClose(dirHandle);
#else
			closedir(dirHandle);
#endif
			dirHandle = NULL;
		}
}


string diru::first( )
{
	_close();
	string pattern = dirName ;

#ifdef WIN32
	if ( dirName[dirName.length()-1 ] == '\\' )
		pattern +=  '*';
	else
		pattern +=  "\\*";

    std::wstring wpattern = miniutil::MiniConv::String_Utf8_To_UCS2(pattern);

	WIN32_FIND_DATAW dirent;
    dirHandle = FindFirstFileW(wpattern.c_str(), &dirent);

    if(dirHandle == INVALID_HANDLE_VALUE)
        return string("");
    return miniutil::MiniConv::String_UCS2_To_Utf8(std::wstring(dirent.cFileName));
#else
	if ( dirName[dirName.length()-1 ] != '/' )
		pattern += '/';

	dirHandle = opendir( pattern.c_str() );
	if ( dirHandle == NULL )
		return string("");

	struct dirent* entry =  readdir(dirHandle);
	if ( entry == NULL )
		return string("");

	return string( entry->d_name );

#endif

}

string diru::next()
{
	if ( dirHandle == NULL )
		return string("");

#ifdef WIN32
	WIN32_FIND_DATAW dirent;

	if ( ! FindNextFileW(dirHandle, &dirent) )
	{
		_close();
		return string("");
	}
    return miniutil::MiniConv::String_UCS2_To_Utf8(std::wstring(dirent.cFileName));

#else
	struct dirent* entry =  readdir(dirHandle);
	if ( entry == NULL )
	{
		_close();
		return string("");
	}

	return string( entry->d_name );

#endif
}

void diru::getAll( vector<string> & list)
{
	string filename = first();
	while( filename.length() > 0 )
	{
		list.push_back( filename );
		filename = next();
	}
}

string diru::getTempDir()
{
#ifdef WIN32
	wchar_t buf[MAX_PATH+1];
	long l = ::GetTempPathW(MAX_PATH, buf );
	buf[l] = '\0';
    return miniutil::MiniConv::String_UCS2_To_Utf8(std::wstring(buf));
#else
	return string("/tmp");
#endif
}

int diru::deltree( const char * dirname )
{
	// ���ֻ����һ����������û�п��Ǹ��ӵ����
	int count = 0;
	fileu curfile(dirname );
	if ( curfile.isFile() )
	{
		if ( curfile.remove() )
			count ++;
	}
	else if ( curfile.isDir() )
	{
		diru curdir( dirname );
		string nfilename = curdir.first();
		while( nfilename.length() > 0 )
		{
			if ( nfilename != "." && nfilename != ".." )
			{
				string subdir = dirname;
				subdir += diru::SEP;
				subdir += nfilename;

				count += diru::deltree( subdir.c_str() );
			}
			nfilename = curdir.next();
		}
	}
	if ( curfile.remove() )
			count ++;

	if ( curfile.isExist() )
		return -1;
	return count;
}

int diru::mkdir( const string & dirname )
{
    if(dirname.empty())
        return 0;
	int ret = 0;
	std::vector<std::string> vsubdirs;
#ifdef WIN32
	wchar_t Buffer[MAX_PATH+1];
	vsubdirs = miniutil::strutil::split(dirname, "/\\");
	if(vsubdirs[0].find(":") != std::string::npos)
	{
		GetCurrentDirectoryW(MAX_PATH, Buffer);
        std::wstring abspath = miniutil::MiniConv::String_Utf8_To_UCS2(vsubdirs[0]+"/");
		SetCurrentDirectoryW(abspath.c_str());
		vsubdirs.erase(vsubdirs.begin());
	}
#else
	vsubdirs = miniutil::strutil::split(dirname, "/");
    if(dirname[0] == '/')
        vsubdirs[0] = std::string("/")+vsubdirs[0];
#endif
	std::string subdir;
	for(unsigned int i=0; i< vsubdirs.size(); i++)
	{
		if(vsubdirs[i].empty())
			continue;
		subdir += vsubdirs[i];
		miniutil::fileu tmpfile(subdir);
		if(tmpfile.isFile())
		{
			ret = -1;
			break;
		}
		else if(tmpfile.isDir())
		{
			subdir += "/";
			continue;
		}
#ifdef WIN32
        ret = ::_wmkdir( miniutil::MiniConv::String_Utf8_To_UCS2(subdir).c_str());
#else
		ret = ::mkdir( subdir.c_str() ,  S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH );
#endif
		if(ret == -1)
		{
			ret = -1;
			break;
		}
		subdir += "/";
	}
#ifdef WIN32
	SetCurrentDirectoryW(Buffer);
#endif
	return 0;
}

int diru::mkdir( const char * dirname )
{
	return mkdir(std::string(dirname));
}

/*

string & diru::getModuleName()
{
	static string exeName;
	
	if ( exeName.length() >  0 )
		return exeName;

#ifdef WIN32
	char buf[MAX_PATH+1];
	DWORD size = ::GetModuleFileName( NULL,  buf, MAX_PATH );

	for( unsigned int i=0; i< size; i++ )
		if ( buf[i] == '\\' ) buf[i] = '/';

	buf[size] = 0;
	exeName = buf;
#else
	char exePath[MAX_PATH+1];
	char fullPath[MAX_PATH+1];
	sprintf(exePath,"/proc/%d/exe",getpid());
	readlink(exePath,fullPath,MAX_PATH );

	exeName = fullPath;
#endif
	return exeName;
}

string & diru::getModuleDir()
{
	static string exeDir;

	if ( exeDir.length() > 0 )
		return exeDir;

	string & exeName = diru::getModuleName();

	string::size_type lastsep = exeName.find_last_of( "\\/" );
	if ( lastsep != string::npos )
		exeDir = exeName.substr(0, lastsep+1 );

	return exeDir;
}

string & diru::getModuleBaseName()
{
	static string baseName;

	if ( baseName.length() > 0 )
		return baseName;

	string & exeName = diru::getModuleName();

    string::size_type lastdot = exeName.find_last_of( "." );
    if ( lastdot == string::npos )
    {
        baseName = exeName;
        return baseName;
    }

	string::size_type lastsep = exeName.find_last_of( "\\/" );

	if ( lastsep != string::npos && lastsep > lastdot )
    {
        baseName = exeName;
        return baseName;
    }

	baseName = exeName.substr(0, lastdot );
	return baseName;
}

string diru::getCurrentDir()
{
	char buf[MAX_PATH+1];

	::getcwd( buf, MAX_PATH );
#ifdef WIN32
	size_t size = strlen(buf);
	for(unsigned int i=0; i< size; i++ )
		if ( buf[i] == '\\' ) buf[i] = '/';
#endif

	return string(buf);
}

string  diru::getAbsolutePathByModule( const string & path )
{
	if ( path[0] == '/' )
		return path;
#ifdef WIN32
	if ( path[1] == ';' )
		return path;

	string npath = path;
	for(unsigned int i=0; i<npath.length(); i++ )
		if ( npath[i] == '\\' ) npath[i] = '/';
	string mdir = diru::getModuleDir();
	mdir += npath;
#else
	string mdir = diru::getModuleDir();
	mdir += path;
#endif

	return mdir;
}

string  diru::getAbsolutePathByCwd( const string & path )
{
	if ( path[0] == '/' )
		return path;
#ifdef WIN32
	if ( path[1] == ';' )
		return path;

	string npath = path;
	for(unsigned int i=0; i<npath.length(); i++ )
		if ( npath[i] == '\\' ) npath[i] = '/';
	string mdir = diru::getCurrentDir();
	mdir += "/";
	mdir += npath;
#else
	string mdir = diru::getCurrentDir();
	mdir += "/";
	mdir += path;
#endif

	return mdir;
}
string  diru::getAppDataPath()
{
#ifdef WIN32
    char          szDir[MAX_PATH];

    if(SUCCEEDED( SHGetSpecialFolderPath(NULL, szDir, CSIDL_APPDATA, TRUE )))
    {
		return szDir;
    }
	else if ( ::getenv("APPDATA") != NULL )
	{
		return ::getenv("APPDATA");
	}
#endif
	char * p_home = ::getenv("HOME");
	if ( p_home == NULL )
		return ".";

	std::string app_data = p_home;
	app_data += "/.appdata";
	diru::mkdir( app_data );

	return app_data;
}

*/

fileu::fileu( const char * fi )
{
    fileName = fi;
    length = 0;
    lastModify = 0;
    type = -1;
    exist = false;
	_getInfo();
}

fileu::fileu( const string &fi )
{
    fileName = fi;
    length = 0;
    lastModify = 0;
    type = -1;
    exist = false;
	while( fileName[ fileName.length() - 1 ] == diru::SEP[0]  )
	{
		fileName = fileName.substr( 0, fileName.length() - 1 );
	}

	_getInfo();
}

void fileu::_getInfo()
{
#ifdef WIN32
    std::wstring wname = miniutil::MiniConv::String_Utf8_To_UCS2(fileName);
	WIN32_FIND_DATAW ffblk;
    
	//HANDLE hFind = FindFirstFileW( (std::wstring(L"\\\\?\\")+wname).c_str(), &ffblk);
    HANDLE hFind = FindFirstFileW(wname.c_str(), &ffblk);
    int errcode;
	if (hFind != INVALID_HANDLE_VALUE) 
	{
		type = (ffblk.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? TYPE_DIR : TYPE_FILE ;
		length = ffblk.nFileSizeHigh;
		length = (length << 32);
		length += ffblk.nFileSizeLow;
		lastModify = ((m_int64_t)ffblk.ftLastWriteTime.dwLowDateTime |
                ((long long)ffblk.ftLastWriteTime.dwHighDateTime << 32)) / (10000) - (11644473600000);
		lastAccess = ((m_int64_t)ffblk.ftLastAccessTime.dwLowDateTime |
                ((long long)ffblk.ftLastAccessTime.dwHighDateTime << 32)) / (10000) - (11644473600000);
		exist = true;
		FindClose(hFind);
	}
    else
    {
        errcode = GetLastError();
    }
#else
	struct stat info;
	if (stat( fileName.c_str(), &info) == 0) 
	{
		length = info.st_size;
		if ( S_ISREG(info.st_mode) ) type = TYPE_FILE;
		if ( S_ISDIR(info.st_mode) ) type = TYPE_DIR;
		lastModify = (m_int64_t) info.st_mtime * (1000);
		lastAccess = (m_int64_t) info.st_atime * (1000);
		exist = true;
    }

#endif
}


bool fileu::remove()
{
#ifdef WIN32
    std::wstring wname = miniutil::MiniConv::String_Utf8_To_UCS2(fileName);
	if ( this->isFile() )
        return (DeleteFileW( wname.c_str() ) != 0);
	else if ( this->isDir() )
        return (RemoveDirectoryW(wname.c_str() ) != 0);
#else
	const char *filename = this->fileName.c_str();
	if ( this->isFile() )
		return (::remove( filename ) == 0);
	else if ( this->isDir() )
		return (::rmdir( filename ) == 0);

#endif
	return false;
}

bool fileu::remove( const char * filename )
{
	fileu f(filename);

	return f.remove();
}

int fileu::rename(const char* oldnm, const char* newnm)
{
#ifdef WIN32
    std::string soldnm = miniutil::strutil::replace(oldnm, "/", "\\");
    std::string snewnm = miniutil::strutil::replace(newnm, "/", "\\");
    std::wstring woldname = miniutil::MiniConv::String_Utf8_To_UCS2(soldnm);
    std::wstring wnewname = miniutil::MiniConv::String_Utf8_To_UCS2(snewnm);
    BOOL bret = ::MoveFileW(woldname.c_str(), wnewname.c_str());
    if(bret)
        return 0;
    else
        return -1;
#else
    return ::rename(oldnm, newnm); //c99 lib
#endif
}

int fileu::copy(const char* oldnm, const char* newnm)
{
#ifdef WIN32
    std::string soldnm = miniutil::strutil::replace(oldnm, "/", "\\");
    std::string snewnm = miniutil::strutil::replace(newnm, "/", "\\");
    std::wstring woldname = miniutil::MiniConv::String_Utf8_To_UCS2(soldnm);
    std::wstring wnewname = miniutil::MiniConv::String_Utf8_To_UCS2(snewnm);
    BOOL bret = ::CopyFileW(woldname.c_str(), wnewname.c_str(), TRUE);
    if(bret)
        return 0;
    else
        return -1;
#else
    std::stringstream ss;
    ss<<"cp "<<oldnm<<" "<<newnm;
    system(ss.str().c_str());
    return 0;
#endif
}

FILE* fileu::open(const char* filename, const char* option)
{
#ifdef WIN32
    std::wstring wname = miniutil::MiniConv::String_Utf8_To_UCS2(filename);
    std::wstring woption = miniutil::MiniConv::String_Utf8_To_UCS2(option);
    return _wfopen(wname.c_str(), woption.c_str());

    //DWORD access;
    //DWORD openflg = OPEN_EXISTING;
    //int osfflag = 0;
    //std::string sopt = option;

    //if(sopt.find("r") != std::string::npos)
    //{
    //    access = GENERIC_READ;
    //    openflg = OPEN_EXISTING;
    //    osfflag = _O_RDONLY;
    //    if(sopt.find("+") != std::string::npos)
    //    {
    //        access = GENERIC_READ|GENERIC_WRITE;
    //        osfflag = 0;
    //    }
    //}
    //else if(sopt.find("w") != std::string::npos)
    //{
    //    access = GENERIC_WRITE;
    //    openflg = CREATE_ALWAYS;
    //    if(sopt.find("+") != std::string::npos)
    //    {
    //        access = GENERIC_READ|GENERIC_WRITE;
    //    }
    //}
    //else if(sopt.find("a") != std::string::npos)
    //{
    //    access = GENERIC_WRITE;
    //    openflg = OPEN_ALWAYS;
    //    osfflag = _O_APPEND;
    //    if(sopt.find("+") != std::string::npos)
    //    {
    //        access = GENERIC_READ|GENERIC_WRITE;
    //    }
    //}

    //if(sopt.find("b") == std::string::npos)
    //    osfflag |= _O_TEXT;

    //HANDLE hfile = ::CreateFileW(wname.c_str(), 
    //                    access,
    //                    FILE_SHARE_READ|FILE_SHARE_WRITE,       // share for reading
    //                    NULL,                  // default security
    //                    openflg,         // existing file only
    //                    FILE_ATTRIBUTE_NORMAL, // normal file
    //                    NULL);
    //if(hfile ==  INVALID_HANDLE_VALUE)
    //{
    //    int eret = GetLastError();
    //    return NULL;
    //}

    //if(sopt.find("a") != std::string::npos)
    //    ::SetFilePointer(hfile, 0, 0, FILE_END);
    ////HANDLE h = (HANDLE)_get_osfhandle (_fileno (f));
    ////fdopen();
    //int nHandle = _open_osfhandle((long)hfile, osfflag);
    //if (nHandle == -1)
    //    return NULL;
    ////_open_osfhandle has 64 number limit, from interval.h
    ////_fdopen has 32 max limit, from one const value

    //return _fdopen((int)nHandle, option);
#else
    return fopen(filename, option);
#endif
}
